﻿using System;
using System.Collections;
using System.Text;

namespace SpeakerRecognition
{
    /// <summary>
    /// Class for creating the successive frames and apply Hamming window on each frame
    /// </summary>
    public class CreateFramesCommand : ICommand
    {
        //************************  Private Fields  *******************************// 
        private double[] inputData;
        private double[][] outputData;
        private int overlapSize = 0;
        private int windowSize = 16;
        private bool usingHammingWindow = true;
        private String windowType;
        //************************  Constructors    *******************************// 
        public CreateFramesCommand()
        {
        }

        public CreateFramesCommand(double[] input)
        {
            inputData = input;
        }
        //************************  Public Properties  ****************************// 
        public int OverlapSize
        {
            get { return overlapSize; }
            set { overlapSize = value; }
        }

        public int WindowSize
        {
            get { return windowSize; }
            set
            {
                if (value > inputData.Length)   // check if the window size exceed the data lengths
                {
                    Console.WriteLine("Warning: CreateFramesCommand : windowSize > size of input data !, window size will be set to the input data size");
                    value = inputData.Length;
                }
                windowSize = completeTwoPow(value);
            }
        }

        public double[] InputData
        {
            get { return inputData; }
            set { inputData = value; }
        }

        public double[][] OutputData
        {
            get
            {
                return outputData;
            }
            set { outputData = value; }
        }

        public bool UsingHammingWindow
        {
            get { return usingHammingWindow; }
            set { usingHammingWindow = value; }
        }

        public String WindowType
        {
            get { return windowType; }
            set { windowType = value; }
        }
        //************************  Public Methods  ****************************// 
        #region ICommand Members
        public void execute()
        {
            ArrayList frames = new ArrayList();

            // default overlap size
            //if (overlapSize == 0)
            //    overlapSize = windowSize / 2;

            /*
             *	Loop to create the overlapping frames 
             */
            int j = 0, startIndex = 0;
            while (j < inputData.Length)
            {
                for (j = startIndex; j < windowSize + startIndex; j++)
                {
                    if (j < inputData.Length)
                        frames.Add(inputData[j]);
                    else
                        frames.Add(0.0);      // pad the frame with zeros                           
                }
                startIndex = j - overlapSize;

            }

            // prepare output array 
            outputData = new double[frames.Count / windowSize][];

            double[] window = WindowCreator.createWindowByType(WindowType, windowSize);
            // loop on all existing frames 
            for (j = 0; j < frames.Count / windowSize; j++)
            {
                outputData[j] = new double[windowSize];
                for (int i = 0; i < windowSize; i++)
                {
                    if (usingHammingWindow)
                        OutputData[j][i] = (double)frames[i + j * windowSize] * window[i];
                    else
                        OutputData[j][i] = (double)frames[i + j * windowSize];
                }
            }

        }

        #endregion
        //************************  Private Methods  ****************************// 
        /// <summary>
        /// This method will check if the window size is power of 2 
        /// and if not it will return the closest power of two 
        /// </summary>
        /// <param name="value">window size</param>
        /// <returns>power of two</returns>
        private int completeTwoPow(int value)
        {
            int pow = 1, count = 0,temp = value;

            while (temp > 0)
            {
                if ((temp & 1)==1)
                    count++;
                temp = temp >>1;
                pow = pow << 1;
            }
            if (count > 1)
            {
                Console.WriteLine("Warning: Window size is not power of 2 ! , window size will be set to the closest power of 2 !");
                return pow ;
            }          
            return value;
        }
    }
}
